﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace Zijian666
{
    /// <summary>
    /// 命令行接口
    /// </summary>
    public partial class Ccli
    {

        // 设置窗体为活动窗体
        [DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        // 取消令牌
        private static CancellationTokenSource? _tokenSource;
        // 本地控制台进程
        private static Process? _console;
        // 套接字实例,已连接的命令行程序
        private static Socket? _socket;
        // 命令接收事件
        private static EventHandler<string>? _receiver;
        // 命令缓存
        private static BlockedStringQueue? _blockedQueue;
        // 命令行执行器
        private static CommandLineExecuter? _cli;

        /// <summary>
        /// 打开本地命令行控制台
        /// </summary>
        /// <param name="port">命令行通信端口</param>
        public static void OpenConsole(int port)
        {
            lock (typeof(Ccli))
            {
                if (_console != null)
                {
                    SetForegroundWindow(_console.MainWindowHandle);
                    return;
                }
                // 将必要配置文件写入硬盘, 否则无法启动控制台
                Runtimeconfig.WriteToDisk();
                // 启动控制台
                _console = Process.Start("dotnet", new[] { typeof(Ccli).Assembly.Location, port.ToString() });
                // 等待进程退出后释放资源
                _console.WaitForExitAsync().ContinueWith(t => Disconnect());
            }
        }

        /// <summary>
        /// 检查是否连接命令行程序
        /// </summary>
        /// <returns></returns>
        public static bool CheckConnected() => _socket?.CheckAlive() ?? false;

        /// <summary>
        /// 断开已连接的命令行程序,并销毁资源
        /// </summary>
        public static void Disconnect()
        {
            if (_console != null)
            {
                IgnoreError(_console.Kill);
                IgnoreError(_console.Dispose);
            }
            if (_socket != null)
            {
                IgnoreError(() => _socket.Disconnect(false));
                IgnoreError(_socket.Close);
                IgnoreError(_socket.Dispose);
            }
            if (_blockedQueue != null)
            {
                IgnoreError(_blockedQueue.Dispose);
            }
            if (_tokenSource != null)
            {
                IgnoreError(_tokenSource.Cancel);
                IgnoreError(_tokenSource.Dispose);
            }
            _tokenSource = null;
            _blockedQueue = null;
            _socket = null;
            _console = null;
        }

        /// <summary>
        /// 忽略异常执行委托
        /// </summary>
        private static void IgnoreError(Action action)
        {
            try
            {
                action?.Invoke();
            }
            catch (Exception e)
            {
                Trace.WriteLine(e);
            }
        }

        /// <summary>
        /// 连接命令行程序
        /// </summary>
        /// <param name="port">端口</param>
        /// <param name="address">程序地址,默认为 localhost</param>
        public static Socket? Connect(int port, IPAddress? address = null)
        {
            if (address == null)
            {
                // 默认连接本地命令行程序
                address = IPAddress.Loopback;
            }
            if (_socket != null)
            {
                return _socket.RemoteEndPoint is IPEndPoint endPoint
                    && endPoint.Address.Equals(address)
                    && endPoint.Port == port
                    ? _socket : null;
            }
            lock (typeof(Ccli))
            {
                if (_socket != null)
                {
                    return _socket.RemoteEndPoint is IPEndPoint endPoint
                        && endPoint.Address.Equals(address)
                        && endPoint.Port == port
                        ? _socket : null;
                }
                _tokenSource = new CancellationTokenSource();
                // 创建套接字
                var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                // 连接命令行程序
                socket.Connect(new IPEndPoint(address, port));
                // 绑定接受字符回调
                socket.ListenReceive(OnRceiver, _tokenSource.Token);
                // 发送接入完成消息
                socket.SendLine(Assembly.GetEntryAssembly()?.GetName()?.Name + " 接入完成...");

                if (_cli != null)
                {
                    socket.SendLine("已加载命令行处理程序:");
                    socket.SendLine(_cli.Execute("help")!);
                }
                return _socket = socket;
            }
        }

        /// <summary>
        /// 连接命令行程序, 并持续接收命令
        /// </summary>
        /// <param name="port">端口</param>
        /// <param name="address">程序地址,默认为 localhost</param>
        /// <returns></returns>
        public static IEnumerable<string>? ConnectAndReceives(int port, IPAddress? address = null)
        {
            // 获取套接字
            var socket = Connect(port, address);
            if (socket == null)
            {
                return null;
            }
            // 创建阻塞队列
            if (_blockedQueue != null)
            {
                return null;
            }
            lock (typeof(Ccli))
            {
                if (_blockedQueue != null)
                {
                    return null;
                }
                _blockedQueue = new();
            }

            static IEnumerable<string> receives(BlockedStringQueue queue)
            {
                while (true)
                {
                    // 等待并获取队列中的字符串
                    var str = queue?.WaitDequeue();
                    if (str == null)
                    {
                        break;
                    }
                    yield return str;
                }
            }

            return receives(_blockedQueue);
        }

        /// <summary>
        /// 命令接收事件
        /// </summary>
        public static event EventHandler<string> Rceiver
        {
            add
            {
                _receiver -= value;
                _receiver += value;
            }
            remove { _receiver -= value; }
        }

        /// <summary>
        /// 当收到命令时触发
        /// </summary>
        private static void OnRceiver(object? sender, string command)
        {
            if (_blockedQueue != null)
            {
                _ = _blockedQueue.Enqueue(command);
            }
            _receiver?.Invoke(sender, command);
            var s = _cli?.Execute(command);
            if (s != null)
            {
                _socket?.SendLine(s);
            }
        }
        /// <summary>
        /// 绑定命令行执行对象
        /// </summary>
        /// <param name="cli"></param>
        public static void BindCli(object cli)
        {
            _cli = new CommandLineExecuter(cli);
            if (_socket != null)
            {
                _socket.SendLine("已加载命令行处理程序:");
                _socket.SendLine(_cli.Execute("help")!);
            }
        }
    }
}
